#ifndef _OVERSET_PLOT3D_
#define _OVERSET_PLOT3D_
#include <array>
#include <cassert>
#include <fstream>
#include <iomanip>
#include <memory>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
using exJson = nlohmann::json;

struct CombineOpts {
  std::vector<std::string> bc_fname;
  std::vector<std::string> grid_fname;
  //   bool only_process_neighb;
  std::vector<int> neighb_a2b;
  std::string out_bc_fname;
  std::string out_grid_fname;
};

void initCombineOpts(const std::string &fname, CombineOpts &opt);

/**
 * @brief
 *
 */
struct FaceBc {
  int f_no, face, istart, iend, jstart, jend, kstart, kend, neighb, subface,
      ori;
  double dtheta;
  friend std::istream &operator>>(std::istream &stream, FaceBc &fbc);
  friend std::ostream &operator<<(std::ostream &stream, const FaceBc &fbc);
};
inline std::istream &operator>>(std::istream &stream, FaceBc &fbc) {
  stream >> fbc.f_no >> fbc.face >> fbc.istart >> fbc.iend >> fbc.jstart >>
      fbc.jend >> fbc.kstart >> fbc.kend >> fbc.neighb >> fbc.subface >>
      fbc.ori >> fbc.dtheta;
  return stream;
}

/**
 * @brief
 *
 * @param os
 */
void printFbcHead(std::ostream &os);

/**
 * @brief
 *
 */
class GridBcIn {
  std::vector<std::vector<FaceBc>> blk_fbc_;

public:
  /**
   * @brief 输出到文件
   *
   * @param fname
   */
  void print(const std::string &fname) const;

  void read(const std::string &fname);

  std::vector<FaceBc> &blkBc(int i) {
    assert(-1 < i && i < blk_fbc_.size());
    return blk_fbc_[i];
  }
  const std::vector<FaceBc> &blkBc(int i) const {
    assert(-1 < i && i < blk_fbc_.size());
    return blk_fbc_[i];
  }

  int blockSize() const { return blk_fbc_.size(); }

  /**
   * @brief 追加一个bc_in文件到本文件中
   *
   * @param rhs
   */
  void extend(const GridBcIn &rhs);

  /**
   * @brief 老师的getbc将 farfield设置为其他值，需要手动修改
   * 还可修改重叠边界条件的设置
   * @param a
   * @param b
   */
  void changeNeighbFromA2B(int a, int b);
};

/**
 * @brief 单纯的读入网格，没有虚单元
 *
 */
struct Plot3dBlk {
  int dims[3];
  std::vector<double> x, y, z;
  int blkSize() const { return dims[0] * dims[1] * dims[2]; }
};

/**
 * @brief
 *
 */
class Plot3dGrid {
private:
  std::vector<std::shared_ptr<Plot3dBlk>> blks_;

public:
  /**
   * @brief 读取网格
   *  网格需要以 unformated 格式
   * @param fname
   */
  void readGrid(const std::string &fname);

  int blkSize() const { return blks_.size(); }

  std::shared_ptr<Plot3dBlk> blk(int i) {
    assert(-1 < i && i < blks_.size());
    return blks_[i];
  }

  /**
   * @brief
   *
   * @param fname
   * @param need_close 如果读入网格坐标是不需要关闭网格的
   */
  void readGridMetaData(const std::string &fname);

  /**
   * @brief 将网格以 unformated 格式写出去
   *
   * @param fname
   */
  void writeGrid(const std::string &fname);

  void extend(Plot3dGrid *grids, const int num_grid);
};

/**
 * @brief 检查网格文件与边界条件文件是否对应
 *
 * @param bcin
 * @param grid
 */
void checkBcInAndGrid(const GridBcIn &bcin, Plot3dGrid &grid);

/**
 * @brief 将多个 grid 与 bcin 合并成一个 grid 与 bcin
 *
 * @param bcin
 * @param num_bcin
 * @param out_bcin
 * @param grids
 * @param num_grid
 * @param out_grid
 */
void combine(const GridBcIn *bcin, const int num_bcin, GridBcIn &out_bcin,
             Plot3dGrid *grids, const int num_grid, Plot3dGrid &out_grid);

#endif